use crate::commands::common::{ErrMsg, INT_PARSE_ERR, SYNTAX_ERR};
use std::iter::{Iterator, Peekable};
use std::str::{from_utf8, FromStr};

pub struct CommandParser<I>
where
    I: Iterator,
{
    iter: Peekable<I>,
}

impl<I> CommandParser<I>
where
    I: Iterator,
{
    pub fn new(iter: I) -> Self {
        CommandParser {
            iter: iter.peekable(),
        }
    }

    pub fn raw_peek(&mut self) -> Option<&I::Item> {
        self.iter.peek()
    }

    pub fn raw_take(&mut self) -> Option<I::Item> {
        self.iter.next()
    }

    pub fn good(&mut self) -> bool {
        self.raw_peek().is_some()
    }

    pub fn skip(&mut self, count: usize) -> usize {
        let mut steps = 0;
        while steps < count && self.good() {
            self.raw_take();
            steps += 1;
        }
        steps
    }

    // 判断下一个token是否符合pred
    fn eat_pred<F>(&mut self, pred: F) -> bool
    where
        F: Fn(&I::Item) -> bool,
    {
        if let Some(item) = self.raw_peek() {
            if pred(item) {
                self.raw_take();
                return true;
            }
        }
        false
    }

    pub fn eat_eq_icase(&mut self, expected: &str) -> bool
    where
        I::Item: AsRef<[u8]>,
    {
        self.eat_pred(|item| item.as_ref().eq_ignore_ascii_case(expected.as_ref()))
    }

    // 若下个token与str相等, 则将该token内容存储在flag中
    // 若flag已有内容, 则返回false
    pub fn eat_eq_icase_flag(&mut self, expected: &str, flag: &mut String) -> bool
    where
        I::Item: AsRef<[u8]>,
    {
        if (expected == flag || flag.is_empty()) && self.eat_eq_icase(expected.as_ref()) {
            *flag = expected.to_string();
            return true;
        }
        false
    }

    pub fn take_str(&mut self) -> Result<I::Item, ErrMsg>
    where
        I::Item: Clone,
    {
        if !self.good() {
            return Err(SYNTAX_ERR);
        }
        Ok(self.raw_take().unwrap())
    }

    pub fn take_number<T>(&mut self) -> Result<T, ErrMsg>
    where
        T: FromStr,
        I::Item: AsRef<[u8]>,
    {
        if !self.good() {
            return Err(SYNTAX_ERR);
        }

        if let Some(item) = self.raw_peek() {
            if let Ok(val) = from_utf8(item.as_ref()) {
                if let Ok(i) = val.parse::<T>() {
                    self.raw_take();
                    return Ok(i)
                }
            }
        }
        Err(INT_PARSE_ERR)
    }

    /*pub fn remains<T>(&self) -> usize {
        self.iter.len()
    }*/
}
